#include "es_oled.h"
#include "es_font.h"
#include "rtthread.h"

#if USING_SIM_I2C

void iic_delay()
{
	u8 t = 3;
	while (t--)
		;
}

//模拟 iic START 条件, 内部调用
void simiic_start()
{
	SDA = 1;
	SCL = 1;
	iic_delay();
	/* START:when CLK is high,DATA change form high to low */
	SDA = 0;
	iic_delay();
	SCL = 0;
	iic_delay();
}

//模拟 iic STOP 条件, 内部调用
void iic_stop(void)
{
	SDA = 0;
	SCL = 1;
	//STOP:when CLK is high DATA change form low to high
	iic_delay();
	SDA = 1; //发送I2C总线结束信号
}

//模拟 iic 等待应答, 内部调用
void iic_waitack(void)
{
	SDA = 1;
	iic_delay();
	SCL = 1;
	iic_delay();
	SCL = 0;
	iic_delay();
}

//模拟 iic 传送数据, 内部调用
void iic_writebyte(unsigned char data_t)
{
	unsigned char t;

	for (t = 0; t < 8; t++)
	{
		if (data_t & 0x80) //将dat的8位从最高位依次写入
		{
			SDA = 1;
		}
		else
		{
			SDA = 0;
		}
		iic_delay();
		SCL = 1;
		iic_delay();
		SCL = 0; //将时钟信号设置为低电平
		data_t <<= 1;
	}
}

/**
 * @description: 软件模拟 I2C 发送一个字节, 文件内部调用, 无需关心
 * @param {uint8_t} addr 寄存器地址
 * @param {uint8_t} data 待发数据
 * @return None
 */
void I2C_WriteByte(uint8_t addr, uint8_t data)
{
	simiic_start();
	iic_writebyte(OLED_ADDRESS);
	iic_waitack();
	iic_writebyte(addr);
	iic_waitack();
	iic_writebyte(data);
	iic_waitack();
	iic_stop();
}
#else

void I2C_Configuration(I2C_TypeDef *I2Cx)
{
	I2C_InitTypeDef I2C_InitStructure;
	/* I2C1: PB6 -- SCL; PB7 -- SDA */
	uint32_t RCC_APB1Periph_I2Cx = RCC_APB1Periph_I2C1;
	uint16_t GPIO_Pin_SCL = GPIO_Pin_6;

	/* I2C1: PB10 -- SCL; PB11 -- SDA */
	if (I2C2 == I2Cx)
	{
		RCC_APB1Periph_I2Cx <<= 1;
		GPIO_Pin_SCL <<= 4;
	}

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2Cx, ENABLE);

	gpio_init(GPIOB, GPIO_Pin_SCL | GPIO_Pin_SCL << 1, GPIO_Mode_AF_OD); //I2C必须开漏输出

	I2C_DeInit(I2Cx); //使用I2Cx
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = 0x30; //主机的I2C地址,随便写的
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_ClockSpeed = I2C_CLOCKSPEED; //400K

	I2C_Cmd(I2Cx, ENABLE);
	I2C_Init(I2Cx, &I2C_InitStructure);
}

void I2C_WriteByte(uint8_t addr, uint8_t data)
{
	while (I2C_GetFlagStatus(OLED_I2C, I2C_FLAG_BUSY))
		;

	I2C_GenerateSTART(OLED_I2C, ENABLE); //开启I2C1
	while (!I2C_CheckEvent(OLED_I2C, I2C_EVENT_MASTER_MODE_SELECT))
		; /*EV5,主模式*/

	I2C_Send7bitAddress(OLED_I2C, OLED_ADDRESS, I2C_Direction_Transmitter); //器件地址 -- 默认0x78
	while (!I2C_CheckEvent(OLED_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
		;

	I2C_SendData(OLED_I2C, addr); //寄存器地址
	while (!I2C_CheckEvent(OLED_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
		;

	I2C_SendData(OLED_I2C, data); //发送数据
	while (!I2C_CheckEvent(OLED_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
		;

	I2C_GenerateSTOP(OLED_I2C, ENABLE); //关闭I2C1总线
}
#endif

void WriteCmd(unsigned char I2C_Command) //写命令
{
	I2C_WriteByte(0x00, I2C_Command);
}

void WriteDat(unsigned char I2C_Data) //写数据
{
	I2C_WriteByte(0x40, I2C_Data);
}


void OLED_Init(void)
{
	//rt_thread_delay(100); //这里的延时很重要

	WriteCmd(0xAE); //display off
	WriteCmd(0x20); //Set Memory Addressing Mode
	WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8); //Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //亮度调节 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
	WriteCmd(0xb0 + y);
	WriteCmd(((x & 0xf0) >> 4) | 0x10);
	WriteCmd((x & 0x0f) | 0x01);
}

void OLED_Fill(unsigned char fill_Data) //全屏填充
{
	unsigned char m, n;
	for (m = 0; m < 8; m++)
	{
		WriteCmd(0xb0 + m); //page0-page1
		WriteCmd(0x00);		//low column start address
		WriteCmd(0x10);		//high column start address
		for (n = 0; n < 128; n++)
		{
			WriteDat(fill_Data);
		}
	}
}

void OLED_CLS(void) //清屏
{
	OLED_Fill(0x00);
}

//--------------------------------------------------------------
// Prototype      : void OLED_ON(void)
// Calls          :
// Parameters     : none
// Description    : 将OLED从休眠中唤醒
//--------------------------------------------------------------
void OLED_ON(void)
{
	WriteCmd(0X8D); //设置电荷泵
	WriteCmd(0X14); //开启电荷泵
	WriteCmd(0XAF); //OLED唤醒
}

//--------------------------------------------------------------
// Prototype      : void OLED_OFF(void)
// Calls          :
// Parameters     : none
// Description    : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
//--------------------------------------------------------------
void OLED_OFF(void)
{
	WriteCmd(0X8D); //设置电荷泵
	WriteCmd(0X10); //关闭电荷泵
	WriteCmd(0XAE); //OLED休眠
}

/**
 * @description: 显示单个 6*8 字符
 * @param {unsigned char} x
 * @param {unsigned char} y
 * @param {unsigned char} ch
 * @return None
 * @Sample OLED_ShowChar(0, 0, 'A');
 */
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch)
{
	unsigned char c = 0, i = 0;

	c = ch - 32;
	if (x > 122)
	{
		x = 0;
		y++;
	}
	OLED_SetPos(x, y);
	for (i = 0; i < 6; i++)
		WriteDat(asc2_0806[c][i]);
}

/**
 * @description: 以 (x, y) 为起点显示字符串
 * @param {unsigned char} x (0 ~ 127)
 * @param {unsigned char} y (0 ~ 7)
 * @param {const unsigned char} *ch 字符串的首地址
 * @param {unsigned char} TextSize  字号, 可选8, 12, 16
 * @return None
 * @Sample OLED_ShowChar(0, 0, ch[0], 12);
 */
void OLED_ShowStr(unsigned char x, unsigned char y, const unsigned char *ch, unsigned char TextSize)
{
	unsigned char c = 0, i = 0, j = 0;
	switch (TextSize)
	{
	case 8:
	{
		while (ch[j] != '\0')
		{
			c = ch[j] - 32;
			if (x > 122)
			{
				x = 0;
				y++;
			}
			OLED_SetPos(x, y);
			for (i = 0; i < 6; i++)
				WriteDat(asc2_0806[c][i]);
			x += 6;
			j++;
		}
	}
	break;
	case 12:
	{
		while (ch[j] != '\0')
		{
			c = ch[j] - 32;
			if (x > 122)
			{
				x = 0;
				y++;
			}
			OLED_SetPos(x, y);
			for (i = 0; i < 6; i++)
				WriteDat(asc2_1206[c][i]);
			OLED_SetPos(x, y + 1);
			for (i = 0; i < 6; i++)
				WriteDat(asc2_1206[c][i + 6]);
			x += 6;
			j++;
		}
	}
	break;
	case 16:
	{
		while (ch[j] != '\0')
		{
			c = ch[j] - 32;
			if (x > 120)
			{
				x = 0;
				y++;
			}
			OLED_SetPos(x, y);
			for (i = 0; i < 8; i++)
				WriteDat(asc2_1608[c][i]);
			OLED_SetPos(x, y + 1);
			for (i = 0; i < 8; i++)
				WriteDat(asc2_1608[c][i + 8]);
			x += 8;
			j++;
		}
	}
	break;
	}
}

/**
 * @description: OLED 初始化
 * @param 	None
 * @return	None
 */
void oled_init(void)
{
#if USING_SIM_I2C == 0 //硬件 IIC
	I2C_Configuration(OLED_I2C);
#else //软件模拟 IIc
	gpio_init(OLED_GPIO, (GPIO_Pin_0 << SCL_PIN) | (GPIO_Pin_0 << SDA_PIN), GPIO_Mode_Out_PP);
	SCL = SDA = 1;
#endif

	OLED_Init();

#ifdef OLED_DisplayTurn //翻转显示
	WriteCmd(0xC0);
	WriteCmd(0xA0);
#endif
}
